/*-------------------------------------------------------------------------
 *
 * nodeUnique.c
 *	  处理适当情况下查询唯一性的例程
 *
 * Unique是一个非常简单的节点类型，它只是从其子计划的排序元组流中过滤掉重复元组。它实质上是Group的简化形式：去重功能是相同的。然而，Unique不进行投影或资格检查，因此在不需要这两者的情况下，它的效率略高。（是否值得保持两种计划节点类型的节省是可以争论的。）
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeUnique.c
 *
 *-------------------------------------------------------------------------
 */

/*
 * 接口例程
 *		ExecUnique		- 生成一个唯一的临时关系
 *		ExecInitUnique	- 初始化节点和子节点
 *		ExecEndUnique	- 关闭节点和子节点
 *
 * 备注
 *		假设从子计划返回的元组是以排序顺序到达的。
 */

#include "postgres.h"

#include "executor/executor.h"
#include "executor/nodeUnique.h"
#include "miscadmin.h"
#include "utils/memutils.h"


/* ----------------------------------------------------------------
 *		ExecUnique
 * ----------------------------------------------------------------
 */
static TupleTableSlot *			/* 返回：一个元组或 NULL */
fc_ExecUnique(PlanState *fc_pstate)
{
	UniqueState *fc_node = castNode(UniqueState, fc_pstate);
	ExprContext *fc_econtext = fc_node->ps.ps_ExprContext;
	TupleTableSlot *fc_resultTupleSlot;
	TupleTableSlot *fc_slot;
	PlanState  *fc_outerPlan;

	CHECK_FOR_INTERRUPTS();

	/*
	 * 从节点获取信息
	 */
	fc_outerPlan = outerPlanState(fc_node);
	fc_resultTupleSlot = fc_node->ps.ps_ResultTupleSlot;

	/*
	 * 现在循环，只返回非重复的元组。我们假设元组以排序顺序到达，以便我们可以轻松检测重复项。每组的第一个元组被返回。
	 */
	for (;;)
	{
		/*
		 * 从外部子计划中获取一个元组
		 */
		fc_slot = ExecProcNode(fc_outerPlan);
		if (TupIsNull(fc_slot))
		{
			/* 子计划结束，所以我们完成了 */
			ExecClearTuple(fc_resultTupleSlot);
			return NULL;
		}

		/*
		 * 始终返回子计划中的第一个元组。
		 */
		if (TupIsNull(fc_resultTupleSlot))
			break;

		/*
		 * 否则测试新元组和先前返回的元组是否匹配。
		 * 如果是，则我们循环回去并从子计划中获取另一个新元组。
		 */
		fc_econtext->ecxt_innertuple = fc_slot;
		fc_econtext->ecxt_outertuple = fc_resultTupleSlot;
		if (!ExecQualAndReset(fc_node->eqfunction, fc_econtext))
			break;
	}

	/*
	 * 我们有一个与之前保存的元组（如果有的话）不同的新元组。
	 * 保存并返回它。我们必须复制它，因为源子计划
	 * 不会保证在获取下一个源元组后该源元组仍然可以访问。
	 */
	return ExecCopySlot(fc_resultTupleSlot, fc_slot);
}

/* ----------------------------------------------------------------
 *		ExecInitUnique
 *
 *		这初始化了唯一节点状态结构和
 *		节点的子计划。
 * ----------------------------------------------------------------
 */
UniqueState *
ExecInitUnique(Unique *fc_node, EState *fc_estate, int fc_eflags)
{
	UniqueState *fc_uniquestate;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));

	/*
	 * 创建状态结构
	 */
	fc_uniquestate = makeNode(UniqueState);
	fc_uniquestate->ps.plan = (Plan *) fc_node;
	fc_uniquestate->ps.state = fc_estate;
	fc_uniquestate->ps.ExecProcNode = fc_ExecUnique;

	/*
	 * 创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_uniquestate->ps);

	/*
	 * 然后初始化外部计划
	 */
	outerPlanState(fc_uniquestate) = ExecInitNode(outerPlan(fc_node), fc_estate, fc_eflags);

	/*
	 * 初始化结果槽和类型。唯一节点不进行投影，所以
	 * 适当地初始化此节点的投影信息。
	 */
	ExecInitResultTupleSlotTL(&fc_uniquestate->ps, &TTSOpsMinimalTuple);
	fc_uniquestate->ps.ps_ProjInfo = NULL;

	/*
	 * 预计算内循环的fmgr查找数据
	 */
	fc_uniquestate->eqfunction =
		execTuplesMatchPrepare(ExecGetResultType(outerPlanState(fc_uniquestate)),
							   fc_node->numCols,
							   fc_node->uniqColIdx,
							   fc_node->uniqOperators,
							   fc_node->uniqCollations,
							   &fc_uniquestate->ps);

	return fc_uniquestate;
}

/* ----------------------------------------------------------------
 *		ExecEndUnique
 *
 *		这关闭子计划并释放分配给
 *		此节点的资源。
 * ----------------------------------------------------------------
 */
void ExecEndUnique(UniqueState *fc_node)
{
	/* 清理元组表 */
	ExecClearTuple(fc_node->ps.ps_ResultTupleSlot);

	ExecFreeExprContext(&fc_node->ps);

	ExecEndNode(outerPlanState(fc_node));
}


void ExecReScanUnique(UniqueState *fc_node)
{
	/* 必须清除结果元组，以便返回第一个输入元组 */
	ExecClearTuple(fc_node->ps.ps_ResultTupleSlot);

	/*
	 * 如果子节点的chgParam不为空，则计划将由第一个ExecProcNode重新扫描。
	 */
	if (fc_node->ps.lefttree->chgParam == NULL)
		ExecReScan(fc_node->ps.lefttree);
}
